package com.pikaqiu.demo.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * @author 吕梁山
 * @date 2019/4/16
 */
@Component
public class WeChatUtil {

    private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);

    @Resource
    private RedisUtil redisUtil;

    @Value("${wechat.config.token}")
    private String token;
    
    @Value("${wechat.config.host}")
    private String wxConfigHost;

    /**
     * 微信测试号
     */
    public static final String APP_ID = "wwww";

    /**
     * 微信测试号
     */
    private static final String APP_SECRET = "wwwww";

    /**
     * 创建菜单目录
     */
    private static final String URL_MENU_CREAT = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

    /**
     * 查询菜单目录
     */
    private static final String URL_MENU_GET = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";

    /**
     * 创建个性菜单目录
     */
    private static final String URL_CONDITIONBAL_MENU_CREAT = "https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN";

    /**
     * 删除个性菜单目录
     */
    private static final String URL_CONDITIONBAL_MENU_DELETE = "https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token=ACCESS_TOKEN";

    /**
     * 删除所有菜单目录
     */
    private static final String URL_MENU_DELETE = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";

    /**
     * 获取用户基本信息
     * access_token 调用接口凭证
     * openid 普通用户的标识，对当前公众号唯一
     * lang 返回国家地区语言版本，zh_CN 简体，zh_TW 繁体，en 英语
     */
    private static final String URL_USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

    /**
     * 获取用户信息
     * ACCESS_TOKEN 调用接口凭证
     * OPENID 普通用户的标识，对当前公众号唯一
     * lang 返回国家地区语言版本，zh_CN 简体，zh_TW 繁体，en 英语
     */
    private static final String URL_SNS_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

    /**
     * 获取access_token 该access_token用于调用其他接口
     * APPID 第三方用户唯一凭证
     * APPSECRET 第三方用户唯一凭证密钥，即appsecret
     */
    private static final String URL_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";

    /**
     * 获取jsapi_ticket
     */
    private static final String URL_JSAPI_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

    /**
     * 获取网页授权access_token与基础支持中的access_token不同
     * APPID 第三方用户唯一凭证
     * APPSECRET 第三方用户唯一凭证密钥，即appsecret
     */
    private static final String URL_HTML_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

    /**
     * 获取code
     * APPID 公众号的唯一标识
     * REDIRECT_URI 授权后重定向的回调链接地址， 请使用 urlEncode 对链接进行处理
     *
     *
     *
     * SCOPE 应用授权作用域，snsapi_base （不弹出授权页面，直接跳转，只能获取用户openid），snsapi_userinfo （弹出授权页面，可通过openid拿到昵称、性别、所在地。并且， 即使在未关注的情况下，只要用户授权，也能获取其信息 ）
     * STATE 重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节
     */
    public static final String URL_OAUTH2_AUTHRIZE = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE&connect_redirect=1#wechat_redirect";

    /**
     * 发送模板信息
     */
    private static final String URL_MESSAGE_TEMPLATE_SEND = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";

    /**
     * 创建用户标签
     */
    public static final String URL_TAGS_CREATE = "https://api.weixin.qq.com/cgi-bin/tags/create?access_token=ACCESS_TOKEN";

    /**
     * 获取临时素材
     */
    public static final String URL_MEDIA_GET = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID";


    /**
     * 获取基础access_token
     */
    private String getAccessToken() throws IOException {
        String key = "access_token";
        if (redisUtil.hasKey(key)) {
            logger.info("从缓存中获取access_token");
            return redisUtil.get(key).toString();
        } else {
            logger.info("重新请求获取access_token");
            String url = URL_TOKEN.replaceAll("APPID", APP_ID);
            url = url.replaceAll("SECRET", APP_SECRET);
            String content = HttpUtils.doGet(url);
            JSONObject jsonObject = JSONObject.parseObject(content);
            logger.info("重新获取access_token结果：" + jsonObject);
            String access_token = jsonObject.getString("access_token");
            int expires_in = jsonObject.getInteger("expires_in");
            redisUtil.set(key, access_token, expires_in);
            return access_token;
        }
    }

    /**
     * 获取网页access_token
     */
    private String getNetAccessToken() throws IOException {
        String key = "net_access_token";
        if (redisUtil.hasKey(key)) {
            logger.info("从缓存中获取网页access_token");
            return redisUtil.get(key).toString();
        } else {
            logger.info("重新请求获取网页access_token");
            String url = URL_TOKEN.replaceAll("APPID", APP_ID);
            url = url.replaceAll("SECRET", APP_SECRET);
            String content = HttpUtils.doGet(url);
            JSONObject jsonObject = JSONObject.parseObject(content);
            logger.info("重新获取网页access_token结果：" + jsonObject);
            String access_token = jsonObject.getString("access_token");
            int expires_in = jsonObject.getInteger("expires_in");
            redisUtil.set(key, access_token, expires_in);
            return access_token;
        }
    }

    /**
     * 获取jsapi_ticket
     */
    private String getJsapiTicket() throws IOException {
        String key = "jsapi_ticket";
        if (redisUtil.hasKey(key)) {
            logger.info("从缓存中获取jsapi_ticket");
            return redisUtil.get(key).toString();
        } else {
            logger.info("重新请求获取jsapi_ticket");
            String url = URL_JSAPI_TICKET.replaceAll("ACCESS_TOKEN", getAccessToken());
            String content = HttpUtils.doGet(url);
            logger.info("获取到的jsapi_ticket：" + content);
            JSONObject jsonObject = JSONObject.parseObject(content);
            String jsapi_ticket = jsonObject.getString("ticket");
            int expires_in = jsonObject.getInteger("expires_in");
            redisUtil.set(key, jsapi_ticket, expires_in);
            return jsapi_ticket;
        }
    }

    /**
     * 验签方法
     */
    public boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] str = new String[]{token, timestamp, nonce};
        //排序
        Arrays.sort(str);
        //拼接字符串
        StringBuilder buffer = new StringBuilder();
        for (String aStr : str) {
            buffer.append(aStr);
        }
        //进行sha1加密
        String temp = DesUtils.encode(buffer.toString());
        logger.info("签名校验" + (signature.equals(temp) ? "通过" : "失败"));
        //与微信提供的signature进行匹对
        return signature.equals(temp);
    }

    /**
     * 获取JsapiTicket加密后的字符串
     */
    public String getJsapiTicketSign(String nonceStr, long timestamp, String url) throws IOException {
        String str = "jsapi_ticket=" + getJsapiTicket() + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
        logger.info("加密明文：" + str);
        String temp = DesUtils.encode(str);
        logger.info("加密密文：" + temp);
        return temp;
    }

    /**
     * 获取用户网页授权Access_token
     */
    public static JSONObject getOpenId(String code) throws Exception {
        String url = URL_HTML_ACCESS_TOKEN.replaceAll("APPID", APP_ID);
        url = url.replaceAll("SECRET", APP_SECRET);
        url = url.replaceAll("CODE", code);
        String content = HttpUtils.doGet(url);
        JSONObject jsonObject = JSONObject.parseObject(new String(content.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
        logger.info("获取网页授权Access_token结果" + jsonObject);
        return jsonObject;
    }

    /**
     * 获取用户信息
     */
    public static JSONObject getWxUserInfo(String accessToken, String openId) throws Exception {
        System.out.println("获取用户信息");
        String url = URL_SNS_USER_INFO.replaceAll("OPENID", openId);
        url = url.replaceAll("ACCESS_TOKEN", accessToken);
        String content = HttpUtils.doGet(url);
        JSONObject jsonObject = JSONObject.parseObject(new String(content.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
        System.out.println("获取用户信息结果" + jsonObject);
        return jsonObject;
    }

    /**
     * 获取用户基本信息
     */
    public JSONObject getWxUserBaseInfo(String openId) throws Exception {
        logger.info("获取用户基本信息");
        String url = URL_USER_INFO.replaceAll("OPENID", openId);
        url = url.replaceAll("ACCESS_TOKEN", getAccessToken());
        String content = HttpUtils.doGet(url);
        JSONObject jsonObject = JSONObject.parseObject(content);
        logger.info("获取用户基本信息结果" + jsonObject);
        return jsonObject;
    }

    /**
     * 获取临时素材
     */
    public HttpResponse getMedia(String serverId) throws IOException {
        logger.info("获取临时素材");
        String url = URL_MEDIA_GET.replaceAll("MEDIA_ID", serverId);
        url = url.replaceAll("ACCESS_TOKEN", getAccessToken());
        return HttpUtils.doGetReturnResponse(url);
    }

    /**
     * 向用户推送模板消息
     */
    private void sendTemplateMessage(JSONObject templateMessage) throws Exception {
        logger.info("向用户推送模板消息，消息内容：{}", templateMessage.toJSONString());
        String url = URL_MESSAGE_TEMPLATE_SEND.replaceAll("ACCESS_TOKEN", getAccessToken());
        String content = HttpUtils.sendPostJsonStr(url, templateMessage.toJSONString());
        JSONObject jsonObject = JSONObject.parseObject(content);
        logger.info("向用户推送模板消息结果" + jsonObject);
    }

    /**
     * 创建菜单
     */
    private void createMenu(JSONObject jsonObject) throws IOException {
        redisUtil = new RedisUtil();
        String url = URL_MENU_CREAT.replaceAll("ACCESS_TOKEN", getAccessToken());
        HttpUtils.sendPostJsonStr(url, jsonObject.toJSONString());
    }

    /**
     * 查询菜单
     */
    private void getMenu() throws IOException {
        redisUtil = new RedisUtil();
        String url = URL_MENU_GET.replaceAll("ACCESS_TOKEN", getAccessToken());
        HttpUtils.sendPostJsonStr(url, "");
    }

    /**
     * 创建个性化菜单
     */
    private void createConditionalMenu(JSONObject jsonObject) throws IOException {
        redisUtil = new RedisUtil();
        String url = URL_CONDITIONBAL_MENU_CREAT.replaceAll("ACCESS_TOKEN", getAccessToken());
        HttpUtils.sendPostJsonStr(url, jsonObject.toJSONString());
    }

    /**
     * 删除个性化菜单
     */
    public void deleteConditionalMenu(String menuId) throws IOException {
        redisUtil = new RedisUtil();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("menuid", menuId);
        String url = URL_CONDITIONBAL_MENU_DELETE.replaceAll("ACCESS_TOKEN", getAccessToken());
        HttpUtils.sendPostJsonStr(url, jsonObject.toJSONString());
    }

    /**
     * 删除所有菜单
     */
    private void deleteMenu() throws IOException {
        redisUtil = new RedisUtil();
        JSONObject jsonObject = new JSONObject();
        String url = URL_MENU_DELETE.replaceAll("ACCESS_TOKEN", getAccessToken());
        HttpUtils.sendPostJsonStr(url, jsonObject.toJSONString());
    }


    public static void main(String[] args) throws IOException {
        WeChatUtil weChatUtil = new WeChatUtil();
//        weChatUtil.getMenu();
//        weChatUtil.deleteMenu();
//        weChatUtil.getMenu();
//        weChatUtil.deleteConditionalMenu("467056105");
    }
}
